﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;


/*
 No. of bytes Type [Value] Description
2 U16 framebuffer-width
2 U16 framebuffer-height
16 PIXEL_FORMAT server-pixel-format
4 U32 name-length
name-length U8 array name-string
where PIXEL_FORMAT is
No. of bytes Type [Value] Description
 
1 U8 bits-per-pixel
1 U8 depth
1 U8 big-endian-flag
1 U8 true-colour-flag
2 U16 red-max
2 U16 green-max
2 U16 blue-max
1 U8 red-shift
1 U8 green-shift
1 U8 blue-shift
3 padding 
*/

namespace Networking
{

    /// <summary>
    /// Screen size class (short sizes)
    /// </summary>
    public class Size {

        /// <summary>
        /// The height of the screen
        /// </summary>
        public short Height { get; private set; }

        /// <summary>
        /// The width of the screen
        /// </summary>
        public short Width { get; private set; }

        /// <summary>
        /// Creates a new instance of the size class
        /// </summary>
        /// <param name="Height">The height of the screen</param>
        /// <param name="Width">The width of the screen</param>
        public Size(short Height, short Width) {
            
            this.Height = Height;
            this.Width = Width;
        }

    }

    /// <summary>
    /// Struct that represents all the pixel format information
    /// </summary>
    public class PixelFormat
    {
        public byte     BitsPerPixel    { get; set; }
        public byte     Depth           { get;  set; }
        public bool     IsBigEndian     { get;  set; }
        public bool     IsTrueColor     { get;  set; }
        public short    RedMax          { get;  set; }
        public short    GreenMax        { get;  set; }
        public short    BlueMax         { get;  set; }
        public byte     RedShift        { get;  set; }
        public byte     GreenShift      { get;  set; }
        public byte     BlueShift       { get;  set; }
 
        
        public PixelFormat( byte BitsPerPixel, 
                            byte Depth, 
                            bool IsBigEndian, 
                            bool IsTrueColor,
                            short RedMax,
                            short GreenMax,
                            short BlueMax,
                            byte RedShift,
                            byte GreenShift,
                            byte BlueShift) {

            this.BitsPerPixel = BitsPerPixel;
            this.Depth = Depth;
            this.IsBigEndian = IsBigEndian;
            this.IsTrueColor = IsTrueColor;
            this.RedMax = RedMax;
            this.GreenMax = GreenMax;
            this.BlueMax = BlueMax;
            this.RedShift = RedShift;
            this.GreenShift = GreenShift;
            this.BlueShift = BlueShift;

        }


        // + 3 bytes padding

    }
    /// <summary>
    /// Packet of type ServerInitPacket, it contains all the server initialization parameters
    /// </summary>
    public class ServerInitPacket : Packet
    {

        /// <summary>
        /// The pixel format parameters
        /// </summary>
        public PixelFormat PixelFormat { get; private set; }

        /// <summary>
        /// The framebuffer (screen) size
        /// </summary>
        public Size FramebufferSize { get; private set; } 
        
        /// <summary>
        /// The name of the server
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Creates a new instance of ServerInitPacket specifying all the parameters
        /// </summary>
        /// <param name="PixelFormat">The pixel format parameters</param>
        /// <param name="FramebufferSize">The framebuffer (screen) size</param>
        /// <param name="Name">The name of the server</param>
        public ServerInitPacket(PixelFormat PixelFormat, Size FramebufferSize, string Name) 
            : base(PacketType.ServerInit)
        {
            this.PixelFormat = PixelFormat;
            this.FramebufferSize = FramebufferSize;
            this.Name = Name;

        }

        /// <summary>
        /// Creates a new instance of ServerInitPacket extracting all the parameters from the byte buffer
        /// </summary>
        /// <param name="buffer">The byte buffer containing the packet data</param>
        public ServerInitPacket(byte[] buffer) 
            : base(PacketType.ServerInit, buffer) 
        {
            //
        }

        /// <summary>
        /// Serializes the packet in order to send it
        /// </summary>
        /// <returns>The bytes of the packet</returns>
        public override byte[] Serialize()
        {

            MemoryStream memory = new MemoryStream();

            BinaryWriter writer = new BinaryWriter(memory);

            writer.Write(this.FramebufferSize.Width);
            writer.Write(this.FramebufferSize.Height);
            writer.Write(this.PixelFormat.BitsPerPixel);
            writer.Write(this.PixelFormat.Depth);
            writer.Write((byte)(this.PixelFormat.IsBigEndian ? 0x1 : 0x0));
            writer.Write((byte)(this.PixelFormat.IsTrueColor ? 0x1 : 0x0));
            writer.Write(this.PixelFormat.RedMax);
            writer.Write(this.PixelFormat.GreenMax);
            writer.Write(this.PixelFormat.BlueMax);
            writer.Write(this.PixelFormat.RedShift);
            writer.Write(this.PixelFormat.GreenShift); 
            writer.Write(this.PixelFormat.BlueShift);
            
            // Padding
            writer.Write(new byte[3] { 0, 0, 0 });

            //writer.Write(this.Name.Length);
            writer.Write(this.Name);

            writer.Close();

            return memory.ToArray();

        }


        /// <summary>
        /// Loads a packet from a byte buffer
        /// </summary>
        /// <param name="buffer">The data buffer</param>
        public override void Load(byte[] buffer)
        {
            if (buffer.Length < 22) throw new BufferSizeMismatchException();

            MemoryStream memory = new MemoryStream(buffer);

            BinaryReader reader = new BinaryReader(memory);

            this.FramebufferSize = new Size(reader.ReadInt16(), reader.ReadInt16());

            this.PixelFormat.BitsPerPixel = reader.ReadByte();
            this.PixelFormat.Depth = reader.ReadByte();
            this.PixelFormat.IsBigEndian = (reader.ReadByte() == 0x1) ? true : false;
            this.PixelFormat.IsTrueColor = (reader.ReadByte() == 0x1) ? true : false;            
            this.PixelFormat.RedMax = reader.ReadInt16();
            this.PixelFormat.GreenMax = reader.ReadInt16();
            this.PixelFormat.BlueMax = reader.ReadInt16();
            this.PixelFormat.RedShift = reader.ReadByte();
            this.PixelFormat.GreenShift = reader.ReadByte();
            this.PixelFormat.BlueShift = reader.ReadByte();

            // Padding
            reader.ReadBytes(3);//.Write(new byte[3] { 0, 0, 0 });

            //writer.Write(this.Name.Length);
            this.Name = reader.ReadString();
            
            reader.Close();
        }
    }
}

//1 U8 bits-per-pixel
//1 U8 depth
//1 U8 big-endian-flag
//1 U8 true-colour-flag
//2 U16 red-max
//2 U16 green-max
//2 U16 blue-max
//1 U8 red-shift
//1 U8 green-shift
//1 U8 blue-shift
//3 padding